home *** CD-ROM | disk | FTP | other *** search
Text File | 1999-12-05 | 36.9 KB | 1,242 lines | [TEXT/ALFA] |
- # init.tcl --
- #
- # Default system startup file for Tcl-based applications. Defines
- # "unknown" procedure and auto-load facilities.
- #
- # SCCS: @(#) init.tcl 1.86 97/08/08 10:37:39
- #
- # Copyright (c) 1991-1993 The Regents of the University of California.
- # Copyright (c) 1994-1997 Sun Microsystems, Inc.
- # Some additions copyright (c) 1997-1998 Vince Darley.
-
- set errorCode ""
- set errorInfo ""
-
- if {[info commands tclLog] == ""} {
- proc tclLog {string} {
- message [string trim $string "\r"]
- }
- }
- if {[info tclversion] >= 8.0} {
- namespace eval index {}
- namespace eval procs {}
- # used to force some child namespaces into existence
- ;proc namesp {var} {
- if {[catch "uplevel global $var"]} {
- set ns ""
- while {[regexp "^(::)?($ns\[a-zA-Z_\]+::)" $var ns]} {
- uplevel "namespace eval $ns {}"
- }
- }
- }
- } else {
- ;proc namesp {var} {}
- rename load evaluate
- }
-
- # 7.1 doesn't rename unbind in the actual application
- if {[info commands unBind] == ""} { rename unbind unBind }
-
- # define compatibility procs for menu, bind, unbind
- if {[info commands bind] == ""} {
- proc bind {args} { uplevel 1 Bind $args }
- proc unbind {args} { uplevel 1 unBind $args }
- proc menu {args} {
- regsub -all "\{menu " $args "\{Menu " args
- uplevel 1 Menu $args
- }
- }
- namespace eval file {}
- # determine platform specific directory symbol
- regexp {Z(.)Z} [file join Z Z] "" file::separator
- # To get rid of the stupid {} variable created by the above line.
- # We 'catch' in case a future version of Tcl fixes this silliness.
- catch {unset {}}
-
- ##
- # -------------------------------------------------------------------------
- #
- # "unknown" --
- #
- # Almost the same as standard Tcl 8 unknown. Since we're on a Mac,
- # I removed the auto_execok flag, and for some reason had to change
- # 'history change $newcmd 0' to 'history change $newcmd'
- # -------------------------------------------------------------------------
- ##
- # unknown --
- # This procedure is called when a Tcl command is invoked that doesn't
- # exist in the interpreter. It takes the following steps to make the
- # command available:
- #
- # 1. See if the autoload facility can locate the command in a
- # Tcl script file. If so, load it and execute it.
- # 2. If the command was invoked interactively at top-level:
- # (a) see if the command exists as an executable UNIX program.
- # If so, "exec" the command.
- # (b) see if the command requests csh-like history substitution
- # in one of the common forms !!, !<number>, or ^old^new. If
- # so, emulate csh's history substitution.
- # (c) see if the command is a unique abbreviation for another
- # command. If so, invoke the command.
- #
- # Arguments:
- # args - A list whose elements are the words of the original
- # command, including the command name.
- proc unknown args {
- global auto_noload env unknown_pending tcl_interactive
- global errorCode errorInfo
-
- # Save the values of errorCode and errorInfo variables, since they
- # may get modified if caught errors occur below. The variables will
- # be restored just before re-executing the missing command.
-
- set savedErrorCode $errorCode
- set savedErrorInfo $errorInfo
- set name [lindex $args 0]
- if {![info exists auto_noload]} {
- #
- # Make sure we're not trying to load the same proc twice.
- #
- if {[info exists unknown_pending($name)]} {
- return -code error "self-referential recursion in \"unknown\" for command \"$name\"";
- }
- set unknown_pending($name) pending;
- set ret [catch {auto_load $name} msg]
- unset unknown_pending($name);
- if {$ret != 0} {
- return -code $ret -errorcode $errorCode \
- "error while autoloading \"$name\": $msg"
- }
- if {![array size unknown_pending]} {
- unset unknown_pending
- }
- if {$msg} {
- set errorCode $savedErrorCode
- set errorInfo $savedErrorInfo
- set code [catch {uplevel 1 $args} msg]
- if {$code == 1} {
- #
- # Strip the last five lines off the error stack (they're
- # from the "uplevel" command).
- #
-
- set new [split $errorInfo \n]
- set new [join [lrange $new 0 [expr {[llength $new] - 6}]] \n]
- return -code error -errorcode $errorCode \
- -errorinfo $new $msg
- } else {
- return -code $code $msg
- }
- }
- }
- if {([info level] == 1) && ([info script] == "") \
- && [info exists tcl_interactive] && $tcl_interactive} {
- set errorCode $savedErrorCode
- set errorInfo $savedErrorInfo
- if {$name == "!!"} {
- set newcmd [history event]
- } elseif {[regexp {^!(.+)$} $name dummy event]} {
- set newcmd [history event $event]
- } elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$} $name dummy old new]} {
- set newcmd [history event -1]
- catch {regsub -all -- $old $newcmd $new newcmd}
- }
- if {[info exists newcmd]} {
- tclLog "\r$newcmd"
- history change $newcmd
- return [uplevel $newcmd]
- }
-
- set ret [catch {set cmds [info commands $name*]} msg]
- if {[string compare $name "::"] == 0} {
- set name ""
- }
- if {$ret != 0} {
- return -code $ret -errorcode $errorCode \
- "error in unknown while checking if \"$name\" is a unique command abbreviation: $msg"
- }
- if {[llength $cmds] == 1} {
- return [uplevel [lreplace $args 0 0 $cmds]]
- }
- if {[llength $cmds] != 0} {
- if {$name == ""} {
- return -code error "empty command name \"\""
- } else {
- return -code error \
- "ambiguous command name \"$name\": [lsort $cmds]"
- }
- }
- }
- return -code error "invalid command name \"$name\""
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "auto_load" --
- #
- # I use this separate proc to be closer to the standard Tcl 8 system
- # of unknown-loading.
- # -------------------------------------------------------------------------
- ##
- proc auto_load cmd {
- set f [procs::find $cmd]
- if {$f != ""} {
- uplevel \#0 source [list $f]
- return [expr {[llength [info commands $cmd]] != 0}]
- }
- if {[regsub {^::} $cmd "" cmd]} {
- set f [procs::find $cmd]
- if {$f != ""} {
- uplevel \#0 source [list $f]
- return [expr {[llength [info commands $cmd]] != 0}]
- }
- }
- # to cope with some Tcl 8 package stuff
- if {[info tclversion] < 8.0} {
- return 0
- }
- global auto_index auto_oldpath auto_path
-
- set namespace [uplevel {namespace current}]
- set nameList [auto_qualify $cmd $namespace]
- # workaround non canonical auto_index entries that might be around
- # from older auto_mkindex versions
- lappend nameList $cmd
- foreach name $nameList {
- if {[info exists auto_index($name)]} {
- uplevel #0 $auto_index($name)
- return [expr {[info commands $name] != ""}]
- }
- }
- if {![info exists auto_path]} {
- return 0
- }
-
- if {![auto_load_index]} {
- return 0
- }
-
- foreach name $nameList {
- if {[info exists auto_index($name)]} {
- uplevel #0 $auto_index($name)
- if {[info commands $name] != ""} {
- return 1
- }
- }
- }
- return 0
- }
-
- # auto_load_index --
- # Loads the contents of tclIndex files on the auto_path directory
- # list. This is usually invoked within auto_load to load the index
- # of available commands. Returns 1 if the index is loaded, and 0 if
- # the index is already loaded and up to date.
- #
- # Arguments:
- # None.
-
- proc auto_load_index {} {
- global auto_index auto_oldpath auto_path errorInfo errorCode
-
- if {[info exists auto_oldpath]} {
- if {$auto_oldpath == $auto_path} {
- return 0
- }
- }
- set auto_oldpath $auto_path
-
- # Check if we are a safe interpreter. In that case, we support only
- # newer format tclIndex files.
-
- set issafe [interp issafe]
- for {set i [expr {[llength $auto_path] - 1}]} {$i >= 0} {incr i -1} {
- set dir [lindex $auto_path $i]
- set f ""
- if {$issafe} {
- catch {source [file join $dir tclIndex]}
- } elseif {[catch {set f [open [file join $dir tclIndex]]}]} {
- continue
- } else {
- set error [catch {
- set id [gets $f]
- if {$id == "# Tcl autoload index file, version 2.0"} {
- eval [read $f]
- } elseif {$id == \
- "# Tcl autoload index file: each line identifies a Tcl"} {
- while {[gets $f line] >= 0} {
- if {([string index $line 0] == "#")
- || ([llength $line] != 2)} {
- continue
- }
- set name [lindex $line 0]
- set auto_index($name) \
- "source [file join $dir [lindex $line 1]]"
- }
- } else {
- error \
- "[file join $dir tclIndex] isn't a proper Tcl index file"
- }
- } msg]
- if {$f != ""} {
- close $f
- }
- if {$error} {
- error $msg $errorInfo $errorCode
- }
- }
- }
- return 1
- }
-
- # auto_qualify --
- #
- # Compute a fully qualified names list for use in the auto_index array.
- # For historical reasons, commands in the global namespace do not have leading
- # :: in the index key. The list has two elements when the command name is
- # relative (no leading ::) and the namespace is not the global one. Otherwise
- # only one name is returned (and searched in the auto_index).
- #
- # Arguments -
- # cmd The command name. Can be any name accepted for command
- # invocations (Like "foo::::bar").
- # namespace The namespace where the command is being used - must be
- # a canonical namespace as returned by [namespace current]
- # for instance.
-
- proc auto_qualify {cmd namespace} {
-
- # count separators and clean them up
- # (making sure that foo:::::bar will be treated as foo::bar)
- set n [regsub -all {::+} $cmd :: cmd]
-
- # Ignore namespace if the name starts with ::
- # Handle special case of only leading ::
-
- # Before each return case we give an example of which category it is
- # with the following form :
- # ( inputCmd, inputNameSpace) -> output
-
- if {[regexp {^::(.*)$} $cmd x tail]} {
- if {$n > 1} {
- # ( ::foo::bar , * ) -> ::foo::bar
- return [list $cmd]
- } else {
- # ( ::global , * ) -> global
- return [list $tail]
- }
- }
-
- # Potentially returning 2 elements to try :
- # (if the current namespace is not the global one)
-
- if {$n == 0} {
- if {[string compare $namespace ::] == 0} {
- # ( nocolons , :: ) -> nocolons
- return [list $cmd]
- } else {
- # ( nocolons , ::sub ) -> ::sub::nocolons nocolons
- return [list ${namespace}::$cmd $cmd]
- }
- } else {
- if {[string compare $namespace ::] == 0} {
- # ( foo::bar , :: ) -> ::foo::bar
- return [list ::$cmd]
- } else {
- # ( foo::bar , ::sub ) -> ::sub::foo::bar ::foo::bar
- return [list ${namespace}::$cmd ::$cmd]
- }
- }
- }
-
- # auto_mkindex:
- # Regenerate a tclIndex file from Tcl source files. Takes two arguments:
- # the name of the directory in which the tclIndex file is to be placed,
- # and a glob pattern to use in that directory to locate all of the relevant
- # files.
- proc auto_mkindex {dir {files *.tcl}} {
- # Due to some peculiarities with current working directories
- # under some MacOS/HFS+/other conditions, we avoid using
- # 'cd' and 'pwd' explicitly if possible.
- set dir [file nativename $dir]
- global tcl_platform
- switch -- $tcl_platform(platform) {
- "macintosh" {
- if {$dir == ":" || $dir == "."} {
- set dir [pwd]
- }
- }
- default {
- if {$dir == "."} {
- set dir [pwd]
- }
- }
- }
- set relative 1
- foreach volume [file volumes] {
- if {[string first $volume $dir] == 0} {
- unset relative
- break
- }
- }
- if {[info exists relative]} {
- set dir [file join [pwd] $dir]
- unset relative
- }
- # So we can handle relative path names
- if {[file pathtype $dir] == "relative"} {
- set dir [file join [pwd] $dir]
- }
- if {![catch {file readlink $dir} _root]} {
- set dir $_root
- }
- set dir [string trim $dir :]
- append line "# Tcl autoload index file: each line\
- identifies a file (nowrap)\n\n"
- set indexvar "[file tail [string trim $dir :]]_index"
- append line "set \"${indexvar}\" \{\n"
-
- set cid [scancontext create]
- # This pattern is used to extract procedures when the 'scanfile'
- # command is used below. We don't do anything too dramatic if
- # the procedure name can't be extracted. The most likely cause
- # is a garbled file.
- scanmatch $cid "^\[ \t\]*proc\[ \t\]" {
- if {[regexp "^\[ \t\]*proc\[ \t\]+((\"\[^\"\]+\")|(\{\[^\}\]+\})|(\[^ \t\]*))" \
- $matchInfo(line) match procName]} {
- append line "$procName "
- } else {
- message "Couldn't extract a proc from '$matchInfo(line)'!"
- }
- }
- foreach file [glob -dir $dir -- $files] {
- watchCursor
- set f ""
- append line "\{[file tail $file]\14 "
- message [file tail $file]
- if {[catch {open $file r} fid]} {
- lappend errors $fid
- lappend errorFiles $file
- } else {
- if {[catch {scanfile $cid $fid} err]} {
- lappend errors $err
- lappend errorFiles $file
- }
- close $fid
- }
- append line "\}\n"
- }
-
- scancontext delete $cid
-
- append line "\}\n"
- if {[info exists errors]} {
- if {[dialog::yesno -y "View the error" -n "Continue" \
- "The following files: [join $errorFiles ,] were unable\
- to be opened or scanned for procedures to store in Tcl index\
- files. This is a serious error. Alpha will not be\
- able to find procedures stored in those files, and will\
- therefore fail to function correctly. You should\
- ascertain the cause of these\
- problems and fix them. Your disk may be damaged.\r\
- To avoid some of these problems, the Tcl index file\
- in $dir will not be replaced."]} {
- dialog::alert [join $errors "\r"]
- }
- } else {
- if {[catch {open [file join $dir tclIndexx] w} fid]} {
- if {[file exists [file join $dir tclIndex]] \
- && ![file writable $dir]} {
- # This is a read-only directory, so there isn't
- # a problem that we couldn't write to it. Probably
- # it's a system directory such as the base Tcl library.
- message "'$dir' is read-only, so I'll use the existing Tcl index."
- } else {
- dialog::alert "The Tcl index file in $dir could not\
- be rewritten. Perhaps the file is locked or read-only?\
- The old index will be left intact, but you should fix\
- this problem so Alpha can index new files in\
- this directory."
- }
- } else {
- if {[catch {puts -nonewline $fid $line} err]} {
- if {[dialog::yesno -y "View the error" -n "Continue" \
- "The Tcl index file in $dir was successfully opened,\
- but Alpha encountered an error while writing to the\
- file. This is a very serious problem, and Alpha will\
- probably no longer function correctly. At the very\
- least you will need to reinstall that directory, and\
- perhaps all of Alpha."]} {
- dialog::alert $err
- }
- }
- catch {close $fid}
- }
- foreach i [info vars $indexvar] {
- global $i
- unset $i
- }
- }
-
- }
-
- proc procs::find {cmd} {
- global auto_path
- regsub -all {[][\$?^|*+()\{\}]} $cmd {\\&} cmd
- foreach path $auto_path {
- if {![file exists $path]} continue
- if {[info tclversion] < 8.0} {
- if {![catch {file readlink $path} _path]} {
- set path $_path
- }
- } else {
- if {[file type $path] == "link"} {
- if {[catch {set path [file readlink $path]}]} {
- # forget about this one
- continue
- }
- }
- }
- set index "[file tail $path]_index"
- global $index
- if {![info exists $index]} {
- if {![file exists [file join $path tclIndexx]]} continue
- uplevel \#0 source [list [file join $path tclIndexx]]
- if {![info exists $index]} {
- alertnote "Tcl index in $path is incorrectly formed. It\
- should set the variable $index but doesn't. You should\
- fix this problem."
- }
- }
- if {[regexp "\n\{(\[^\14\]+)\14\[^\n\]* \[\"\{\]?(::)?${cmd}\[\"\}\]? " [set $index] dummy file]} {
- return [file join $path $file]
- }
- }
- return ""
- }
- # this proc adds 'dummy' so 'file dirname' works the same
- # way for tcl7.4 and tcl8.0.
- proc alpha::makeAutoPath {{check_dups 1} {skipPrefs 0}} {
- global HOME auto_path
- if {$check_dups} {
- set lcmd lunion
- } else {
- set lcmd lappend
- }
- set root [file join $HOME Tcl]
- if {![catch {file readlink $root} _root]} {
- set root $_root
- }
-
- foreach dir {SystemCode Modes Menus} {
- $lcmd auto_path [file join $root $dir]
- foreach d [glob -t d -nocomplain -dir [file join $root $dir] *] {
- $lcmd auto_path [file dirname "${d}dummy"]
- }
- }
- if {!$skipPrefs} {
- $lcmd auto_path [file join $root Packages]
- $lcmd auto_path [file join $root UserModifications]
- foreach d [glob -t d -nocomplain -dir [file join $root Packages] *] {
- $lcmd auto_path [file dirname "${d}dummy"]
- }
- }
- }
-
- # Clean up temporary files:
- proc removeTemporaryFiles {} {
- global PREFS
- if {[file exists [file join $PREFS tmp]]} {
- foreach f [glob -dir [file join $PREFS tmp] -nocomplain *] {
- message "removing [file tail $f]…"
- file delete $f
- }
- }
- message "all temporary files removed"
- }
- ##
- # -------------------------------------------------------------------------
- #
- # "auto_reset" --
- #
- # After rebuilding indices, Tcl retains its old index information unless
- # we tell it not to.
- # -------------------------------------------------------------------------
- ##
- proc auto_reset {} {
- global auto_path
- foreach path $auto_path {
- if {![file exists $path]} continue
- set index "[file tail $path]_index"
- global $index
- catch {unset $index}
- }
- }
-
- #================================================================================
- # Wonderful procs from Vince Darley (vince@santafe.edu).
- #===============================================================================
-
- if {[info tclversion] < 8.0} {
- proc traceTclProc {{func ""}} {
- global tclMenu
- if {[llength [traceFunc status]]>2} {
- catch {markMenuItem $tclMenu {traceTclProc…} off}
- catch {enableMenuItem $tclMenu dumpTraces off}
- if {[string length [set data [traceDump]]]} {
- if {[dialog::yesno "Dump traces?"]} {
- dumpTraces [string trimright [lindex [traceFunc status] 3] {,}] $data
- }
- }
- traceFunc off
- message "Tracing off."
- return
- }
- if {$func == ""} {
- set func [procs::pick 1]
- }
- if {![string length $func]} return
- traceFunc on $func ""
- catch {markMenuItem $tclMenu {traceTclProc…} on}
- catch {enableMenuItem $tclMenu dumpTraces on}
- message "Tracing '$func'…"
- }
-
-
- proc dumpTraces {{name ""} {data ""}} {
- if {![string length $name]} {
- set name [string trimright [lindex [traceFunc status] 3] {,}]
- }
- if {![string length $data]} {
- set data [traceDump]
- }
-
- if {![string length $data]} {
- message "Trace buffer empty"
- } else {
- new -n "* Trace '$name' *" -m Tcl -info $data
- }
- }
-
- proc procs::traceProc {func} {
- global tclMenu
- # if we're tracing already then clear it
- if {[llength [traceFunc status]]>2} { traceTclProc }
- traceFunc on $func ""
- catch {markMenuItem $tclMenu {traceTclProc…} on}
- catch {enableMenuItem $tclMenu dumpTraces on}
- message "Tracing '$func'…"
- }
-
- proc procs::pick {{try_sel 0}} {
- if {$try_sel && [llength [winNames]] && [string length [set sel [getSelect]]]} {
- if {[info procs $sel] == "$sel"} {
- return $sel
- } else {
- return [listpick -L $sel -p {Func Name:} [lsort -ignore [info procs]]]
- }
- } else {
- return [listpick -p {Func Name:} [lsort -ignore [info procs]]]
- }
- }
-
- } else {
- proc procs::traceProc {func} {
- uplevel traceTclProc $func
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "procs::pick" --
- #
- # -------------------------------------------------------------------------
- ##
- proc procs::pick {{try_sel 0}} {
- if {$try_sel && [llength [winNames]] && [string length [set sel [getSelect]]]} {
- if {[llength [uplevel \#0 [list info commands $sel]]] && ![catch {info args $sel}]} {
- return $sel
- }
- } else {
- set sel ""
- }
- set ns ::
- while {1} {
- set procs [lsort -ignore [namespace children $ns]]
- eval lappend procs [lsort -ignore [uplevel \#0 namespace eval $ns [list info procs]]]
- set choice [listpick -L $sel -p "Pick a function or child namespace in '$ns'" $procs]
- if {![regexp {^::} $choice]} {
- if {${ns} == "::"} {
- return "::${choice}"
- } else {
- return "${ns}::${choice}"
- }
- }
- set ns $choice
- }
- }
-
- if {![catch {package require Trace}]} {
- proc traceTclProc {{func ""}} {
- global tclMenu
- set cmd [lindex [tracecommand list] 0]
- if {$cmd != ""} {
- catch {markMenuItem $tclMenu {traceTclProcÉ} off}
- catch {enableMenuItem $tclMenu dumpTraces off}
- dumpTraces $cmd [tracecommand dump $cmd] 1
- tracecommand off $cmd
- message "Tracing off."
- if {$func == ""} {return}
- }
- if {$func == ""} {
- set func [procs::pick 1]
- }
- if {![string length $func]} return
- tracecommand on $func
- catch {markMenuItem $tclMenu {traceTclProcÉ} on}
- catch {enableMenuItem $tclMenu dumpTraces on}
- message "Tracing '$func'É"
- }
- proc dumpTraces {{name ""} {data ""} {ask 0}} {
- if {![string length $name]} {
- set name [lindex [tracecommand list] 0]
- }
- if {![string length $data]} {
- set data [tracecommand dump $name]
- }
-
- if {![string length $data]} {
- message "Trace buffer empty"
- } else {
- if {$ask} {
- if {![dialog::yesno "Dump traces?"]} {return}
- }
- new -n "* Trace '$name' *" -m Tcl -text $data -shell 1 -read-only 1
- }
- }
- } else {
-
-
- ##
- # -------------------------------------------------------------------------
- #
- # "traceTclProc" --
- #
- # Trace and dump still need a little work under Alpha 8.0. Notice that
- # traces are stored in a file, not in memory as in previous versions
- # of Alpha.
- # -------------------------------------------------------------------------
- ##
- proc traceTclProc {{func ""}} {
- global tclMenu alpha::tracingProc alpha::tracingChannel PREFS
- if {[cmdtrace depth] > 0} {
- catch {markMenuItem $tclMenu {traceTclProc…} off}
- catch {enableMenuItem $tclMenu dumpTraces off}
- catch {
- cmdtrace off
- close $alpha::tracingChannel
- set alpha::tracingChannel ""
- }
- if {[file exists [file join $PREFS tmp traceDump]]} {
- dumpTraces "" "" 1
- file delete [file join $PREFS tmp traceDump]
- }
- message "Tracing off."
- if {$func == ""} {return}
- }
- if {$func == ""} {
- set func [procs::pick 1]
- }
- if {![string length $func]} return
- if {![file exists [file join $PREFS tmp]]} {
- file mkdir [file join $PREFS tmp]
- }
- set alpha::tracingChannel [open [file join $PREFS tmp traceDump] w]
- cmdtrace on $alpha::tracingChannel inside $func
- set alpha::tracingProc $func
- catch {markMenuItem $tclMenu {traceTclProc…} on}
- catch {enableMenuItem $tclMenu dumpTraces on}
- message "Tracing '$func'…"
- }
-
-
- proc dumpTraces {{name ""} {data ""} {ask 0}} {
- global alpha::tracingProc alpha::tracingChannel PREFS
- if {![string length $name]} {
- set name $alpha::tracingProc
- }
- if {![string length $data]} {
- set data [file::readAll [file join $PREFS tmp traceDump]]
- if {$alpha::tracingChannel != ""} {
- close $alpha::tracingChannel
- file delete [file join $PREFS tmp traceDump]
- set alpha::tracingChannel [open [file join $PREFS tmp traceDump] w]
- cmdtrace configure $alpha::tracingChannel
- }
- }
-
- if {![string length $data]} {
- message "Trace buffer empty"
- } else {
- if {$ask} {
- if {![dialog::yesno "Dump traces?"]} {return}
- }
- new -n "* Trace '$name' *" -m Tcl -text $data -shell 1 -read-only 1
- }
- }
-
- }
- }
-
-
- proc rebuildTclIndices {} {
- global auto_path
- foreach dir $auto_path {
- # if directory exists
- if {[file isdir $dir]} {
- # if there are any files
- if {![catch {glob -dir $dir *.*tcl} err]} {
- message "Building [file tail $dir] index…"
- # use 'catch' also in case directory is write-protected
- if {[catch {auto_mkindex $dir *.*tcl} err]} {
- message "Problem rebuilding directory $dir : $err"
- }
- } else {
- message "Directory '$dir' contains no Tcl files!"
- }
- } else {
- message "Directory '$dir' doesn't appear to exist."
- }
- }
- message ""
- # make alpha forget its old information so the new stuff is loaded
- # when required.
- catch {auto_reset}
- }
-
- set alpha::rebuilding 0
-
- proc alpha::rebuildPackageIndices {} {
- alpha::makeIndices
- message "Indices and package menu rebuilt."
- }
-
- proc alpha::makeIndices {} {
- # add all new directories to the auto_path
- alpha::makeAutoPath
- # ensure count is correctly set - otherwise we'd probably have to
- # rebuild next time we started up.
- alpha::rectifyPackageCount
- set types {index::feature index::mode index::uninstall index::maintainer index::help index::disable}
- global pkg_file HOME alpha::rebuilding alpha::version file::separator \
- index::oldmode alpha::tclversion
- eval global $types
- # store old mode information so we can check what changed
- catch {cache::readContents index::mode}
- catch {array set index::oldmode [array get index::mode]}
-
- catch {eval cache::delete $types}
- foreach type $types {
- catch {unset $type}
- }
- foreach dir [list SystemCode Modes Menus Packages] {
- lappend dirs "[file join ${HOME} Tcl ${dir}]${file::separator}"
- eval lappend dirs [glob -t d -dir [file join ${HOME} Tcl ${dir}] -nocomplain *]
- }
- if {[file exists [file join ${HOME} AlphaCore]]} {
- lappend dirs "[file join ${HOME} AlphaCore]${file::separator}"
- }
- set alpha::rebuilding 1
- # provide the 'Alpha' and 'AlphaTcl' packages
- ;alpha::extension Alpha ${alpha::version} {} help {file "Alpha Manual"}
- ;alpha::extension AlphaTcl ${alpha::tclversion} {} help {file "Extending Alpha"}
- # declare 2 different scan contexts:
- set cid_scan [scancontext create]
- scanmatch $cid_scan "^\[ \t\]*alpha::(menu|mode|flag|extension|feature|package\[ \t\]+(uninstall|disable|maintainer|help))\[ \t\\\\\]" {
- incr rebuild_cmd_count 1
- }
- scanmatch $cid_scan "^\[ \t\]*newPref\[ \t\]" {
- if {[incr numprefs] == 1} {
- set newpref_start $matchInfo(offset)
- }
- }
- set cid_help [scancontext create]
- scanmatch $cid_help "^\[ \t\]*#" {
- if {[expr {$linenum +1}] != $matchInfo(linenum)} { set hhelp "" }
- append hhelp [string trimleft $matchInfo(line) " \t#"] " "
- set linenum $matchInfo(linenum)
- }
- scanmatch $cid_help "^\[ \t\]*newPref\[ \t\]" {
- if {[expr {$linenum +1}] == $matchInfo(linenum)} {
- if {$hhelp != ""} {
- set pkg [lindex $matchInfo(line) 4]
- # allow comment to over-ride the mode/package
- regexp "^\\((\\w+)\\)\[ \t\]*(.*)\$" $hhelp "" pkg hhelp
- if {$pkg == "" || $pkg == "global"} {
- set prefshelp([lindex $matchInfo(line) 2]) $hhelp
- } else {
- set prefshelp($pkg,[lindex $matchInfo(line) 2]) $hhelp
- }
- }
- }
- set hhelp ""
- if {[incr numprefs -1] == 0} {
- error "done"
- }
- }
-
- global rebuild_cmd_count
- foreach d $dirs {
- foreach f [glob -nocomplain -path $d *.tcl] {
- if {![catch {open $f} fid]} {
- message "scanning [file tail $f]…"
- set numprefs 0
- set rebuild_cmd_count 0
- # check for 'newPref' or 'alpha::package' statements
- scanfile $cid_scan $fid
- if {$numprefs > 0} {
- message "scanning [file tail $f]…($numprefs prefs)"
- incr newpref_start -520
- seek $fid [expr {$newpref_start > 0 ? $newpref_start : 0}]
- set linenum -2
- set hhelp ""
- catch [list scanfile $cid_help $fid]
- }
- close $fid
- if {$rebuild_cmd_count > 0} {
- message "scanning [file tail $f] for packages"
- set pkg_file $f
- if {[catch {uplevel \#0 [list source $f]} res] != 11} {
- if {[askyesno "Had a problem extracting package information from [file tail $f]. View error?"] == "yes"} {
- alertnote [string range $res 0 240]
- }
- }
- }
- }
- }
- }
- catch {unset rebuild_cmd_count}
- set alpha::rebuilding 0
-
- scancontext delete $cid_scan
- scancontext delete $cid_help
- cache::create index::prefshelp variable prefshelp
-
- foreach type $types {
- cache::add $type "variable" $type
- if {$type != "index::feature"} { catch {unset $type} }
- }
- catch {unset index::oldmode}
- catch {unset pkg_file}
- #foreach n [array names index::feature] {}
- global alpha::requirements
- if {[info exists alpha::requirements]} {
- foreach itm ${alpha::requirements} {
- set m [lindex $itm 0]
- set req [lindex $itm 1]
- if {[catch {package::versionCheck [lindex $req 0] [lindex $req 2]} err]} {
- alertnote "$m mode requirements failure: $err You should upgrade that package."
- }
- }
- }
-
- message "Package index rebuilt."
- }
-
- # 'exit' kills Alpha without allowing it to save etc.
- # 'quit' is therefore more mac-like
- rename exit ""
- proc exit {} {quit}
-
- proc alpha::reportError {string} {
- global reportErrors
- if {$reportErrors} {
- alertnote [string range $string 0 200]
- } else {
- global alpha::errorLog
- append alpha::errorLog $string
- }
- }
-
- proc userMessage {{alerts 1} {message ""}} {
- if {$alerts} {
- alertnote $message
- } else {
- message $message
- }
- }
-
- namespace eval flag {}
-
- # Always use this proc, don't mess with 'flag::types' directly.
- proc flag::addType {type} {
- global flag::types
- if {[lsearch -exact ${flag::types} $type] == -1} {
- lappend flag::types $type
- }
- }
-
- # Declare basic preference types
- namespace eval flag {}
- set flag::types [list "flag" "variable" "binding" "menubinding" \
- "file" "io-file" "funnyChars"]
- # Note: other types are triggered by vars ending in 'Colour', 'Color',
- # 'Folder', 'Path', 'Mode', 'Sig', or 'SearchPath'
-
- namespace eval global {}
-
- ##
- # -------------------------------------------------------------------------
- #
- # "newPref" --
- #
- # Define a new preference variable/flag. You can call this procedure
- # either with multiple arguments or with a single list of all the
- # arguments. So 'newPref flag Hey ...' or 'newPref {flag Hey ...}'
- # are both fine.
- #
- # 'type' is one of:
- # 'flag' (on/off only), 'variable' (anything), 'binding' (key-combo)
- # 'menubinding' (key-combo which works in a menu), 'file' (input only),
- # 'io-file' (either input or output). Variables whose name ends in
- # Sig, Folder, Path, Mode, Colour, Color or SearchPath (case matters here)
- # are treated differently, but are still considered of type 'variable'.
- # For convenience this proc will map types sig, folder, color, ...
- # into 'variable' for you, _if_ the variable ends with the correct
- # string.
- #
- # 'name' is the var name,
- #
- # 'val' is its default value (which will be ignored if the variable
- # already has a value)
- #
- # 'pkg' is either 'global' to mean a global preference, or the name
- # of the mode or package (no spaces) for which this is a preference.
- #
- # 'pname' is a procedure to call if this preference is changed by
- # the user (no need to setup a trace). This proc is only called
- # for changes made through prefs dialogs or prefs menus created by
- # Alpha's core procs. Other changes are not traced.
- #
- # Depending on the previous values, there are two optional arguments
- # with the following uses:
- #
- # TYPE:
- #
- # variable:
- #
- # 'options' is a list of items from which this preference takes a single
- # item.
- # 'subopt' is any of 'item', 'index', 'varitem' or 'varindex' or 'array', where
- # 'item' indicates the pref is simply an item from the given list
- # of items, 'index' indicates it is an index into that list, and
- # 'var*' indicates 'items' is in fact the name of a global variable
- # which contains the list. 'array' means take one of the values from an array.
- # If no value is given, 'item' is the default
- #
- # binding:
- #
- # 'options' is the name of a proc to which this item should be bound.
- # If options = '1', then we Bind to the proc with the same name as
- # this variable. Otherwise we do not perform automatic bindings.
- #
- # 'subopt' indicates whether the binding is mode-specific or global.
- # It should either be 'global' or the name of a mode. If not given,
- # it defaults to 'global' for all non-modes, and to mode-specific for
- # all packages. (Alpha tests if something is a mode by the existence
- # of mode::features($mode))
- # -------------------------------------------------------------------------
- ##
- proc newPref {vtype {name {}} {val 0} {pkg "global"} {pname ""} {options ""} {subopt ""}} {
- if {$name == {}} { uplevel 1 newPref $vtype}
-
- global allFlags allVars tclvars modeVars flag::procs \
- flag::type flag::types alpha::earlyPrefs
- # 'link' means link this variable with Alpha's internals.
- if {[regexp {^early(.*)$} $vtype "" vtype]} {
- lappend alpha::earlyPrefs $name
- }
- if {[regexp {^link(.*)$} $vtype "" vtype]} {
- linkVar $name
- # linked variables over-ride differently to normal preferences.
- if {$val != ""} { global $name ; set $name $val }
- }
- set bad 1
- foreach ty ${flag::types} {
- if {[string first $vtype $ty] == 0} {
- set vtype $ty
- set bad 0
- break
- }
- }
- if {$bad} {
- foreach ty {SearchPath Folder Path Mode Colour Color Sig} {
- if {[string first $vtype [string tolower $ty]] == 0} {
- if {[regexp -- "${ty}\$" $name]} {
- set vtype variable
- set bad 0
- break
- } else {
- error "Type '$vtype' requires the variable's name to end in '$ty'"
- }
- }
- }
- if {$bad} {error "Unknown type '$vtype' in call to newPref"}
- }
- if {$pkg == "global"} {
- switch -- $vtype {
- "flag" {
- lappend allFlags $name
- }
- "variable" {
- lappend allVars $name
- }
- default {
- set flag::type($name) $vtype
- lappend allVars $name
- }
- }
-
- global $name mode global::_varMem
- lunion tclvars $name
- if {[info exists mode] && $mode != ""} {
- global ${mode}modeVars
- if {[info exists $name] && [info exists ${mode}modeVars($name)]} {
- # Don't override an existing mode variable which has been
- # copied into the global namespace; instead just place
- # value in the global cache
- set global::_varMem($name) $val
- } else {
- if {![info exists $name]} {set $name $val} else { set val [set $name] }
- }
- } else {
- if {![info exists $name]} {set $name $val} else { set val [set $name] }
- }
- } else {
- global ${pkg}modeVars mode alpha::changingMode
- lunion modeVars $name
-
- if {![info exists ${pkg}modeVars($name)]} {
- set ${pkg}modeVars($name) $val
- } else {
- set val [set ${pkg}modeVars($name)]
- }
- if {!${alpha::changingMode} && ($mode == $pkg)} {
- global $name global::_varMem
- # Need to load up this global cache for when mode changes!
- if {[info exists $name]} {
- set global::_varMem($name) [set $name]
- }
- set $name $val
- }
- switch -- $vtype {
- "flag" {
- if {[lsearch -exact $allFlags $name] == -1} {
- lappend allFlags $name
- }
- }
- "variable" {
- lappend allVars $name
- }
- default {
- set flag::type($name) $vtype
- lappend allVars $name
- }
- }
- }
- # handle 'options'
- if {$options != ""} {
- switch -- $vtype {
- "variable" {
- global flag::list
- if {$subopt == ""} { set subopt "item" }
- if {[lsearch -exact "array item index varitem varindex" $subopt] == -1} {
- error "Unknown list element type '$subopt' in call to newPref."
- }
- set flag::list($name) [list $subopt $options]
- }
- "binding" {
- global flag::binding mode::features
- if {[info exists mode::features($pkg)]} {
- if {$subopt == ""} {
- set subopt $pkg
- } else {
- if {$subopt == "global"} { set subopt "" }
- }
- }
- set flag::binding($name) [list $subopt $options]
- if {$options == 1} { set options $name }
- catch "Bind [keys::toBind $val] [list $options] $subopt"
- }
- }
- }
- # register the 'modify' proc
- if {[string length $pname]} {
- set flag::procs($name) $pname
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "alpha::rectifyPackageCount" --
- #
- # Returns 1 if count has changed
- # -------------------------------------------------------------------------
- ##
- proc alpha::rectifyPackageCount {} {
- global HOME file::separator
- # check things haven't changed
- foreach d {Modes Menus Packages} {
- lappend count [llength [glob -nocomplain -dir [file join ${HOME} Tcl ${d}] "*\{.tcl,${file::separator}\}"]]
- }
- if {![cache::exists index::count[join $count -]]} {
- cache::deletePat index::count*
- cache::create index::count[join $count -]
- return 1
- } else {
- return 0
- }
- }
-
- proc alpha::checkConfiguration {} {
- global alpha::version alpha::tclversion
- if {![cache::exists index::feature] || (![cache::exists index::mode]) \
- || ([alpha::package versions Alpha] != ${alpha::version}) \
- || ([alpha::package versions AlphaTcl] != ${alpha::tclversion})} {
- set rebuild 1
- # If there's no package information stored at all, or if Alpha's
- # version number has changed, zap the cache. This may not be
- # required, but is safer since core-code changes may modify the
- # form of the cache, or change the format of cached menus etc.
- global PREFS
- if {[cache::exists configuration]} {
- # in case we crashed or some other weirdness
- catch {file delete [file join ${PREFS} configuration]}
- # now backup the configuration file
- # Alpha has a bad filesystem bug which can sometimes arise
- # here, so we do this crazy stuff.
- if {[catch {file rename [file join ${PREFS} Cache configuration] \
- [file join ${PREFS} configuration]}]} {
- dialog::alert "You've hit an unfortunate filesystem bug in Alpha.\
- Unfortunately there is no workaround. Alpha will now forget\
- your globally active features, and some other preferences.\r\
- Sorry! This will be fixed in Alpha 8.0."
- }
- rm -r [file join ${PREFS} Cache]
- file mkdir [file join ${PREFS} Cache]
- catch {file rename [file join ${PREFS} configuration] \
- [file join ${PREFS} Cache configuration]}
- } else {
- rm -r [file join ${PREFS} Cache]
- file mkdir [file join ${PREFS} Cache]
- }
- } else {
- set rebuild [alpha::rectifyPackageCount]
- }
- return $rebuild
- }
-
-
-